#include "StdAfx.h"
#include "AIPlayer.h"
#include "Game.h"
#include "AIRandomPlayer.h"
#include "Piece.h"
 

AIPlayer::AIPlayer(int level) : AIRandomPlayer() 
{
	currentMoveChosen = 0;
	this->level = level;
}

Piece * AIPlayer::choosePiece(sf::RenderWindow & window, std::vector<Piece> &m_pieces)
{
	Piece *piece;
	piece = 0;
	if((this->AIRandomPlayer::getTurnCount())<3)
	{
		//choose random
		piece = this->AIRandomPlayer::choosePiece(window, m_pieces);
	}
	else
	{
		//choose minMax
		if(currentMoveChosen != 0)
			*piece = currentMoveChosen->chosenPiece;
		else
			piece = this->AIRandomPlayer::choosePiece(window, m_pieces);

	}
	return piece;
}

void AIPlayer::play(sf::RenderWindow & window, Piece *piece, std::vector<Piece> &m_pieces)
{
	Location *loc;
	if((this->AIRandomPlayer::getTurnCount())<3)
	{
		//Play random
		this->AIRandomPlayer::play(window, piece, m_pieces);
	}
	else
	{
		//min max
		std::cerr << "play: b4 abs " << std::endl;
		std::cerr << "m_pieces: " << &m_pieces << std::endl;
		std::cerr << "level: " << &level << std::endl;
		std::cerr << "currentMoveChosen: " << &currentMoveChosen << std::endl;
		*this->currentMoveChosen = AIPlayer::alphaBetaSearch(m_pieces, level, currentMoveChosen);
		//malloc the new location...
		std::cerr << "play:  b4 malloc " << std::endl;
		loc = (Location *)malloc(sizeof(Location));
		loc->first = currentMoveChosen->location.first;
		loc->second = currentMoveChosen->location.second;
		std::cerr << "Play: Malloc location " << loc << std::endl;
		(*piece).setLocation(*loc);
		this->increaseTurnCount();
	}
}

AIPlayer::Move AIPlayer::alphaBetaSearch(std::vector<Piece> m_pieces, int level, Move *currentMoveChosen)
{
	sf::sleep(sf::milliseconds(2000));
	if(currentMoveChosen==0)
	{
		std::cerr << "ABS:  b4 current init " << std::endl;
		sf::sleep(sf::milliseconds(2000));
		currentMoveChosen = new Move();
		std::cerr << "ABS:  after init " << std::endl;
		sf::sleep(sf::milliseconds(2000));
		currentMoveChosen->child = 0;
		currentMoveChosen->parent = 0;
		currentMoveChosen->rightBrother = 0;
		currentMoveChosen->leftBrother = 0;
		std::cerr << "ABS:  first node " << std::endl;
	}
	currentMoveChosen->m_pieces = m_pieces;
	std::cerr << "ABS:  b4 max " << std::endl;
	int value = maxValue(currentMoveChosen, MINUS_INFINITY, INFINITY, level);
	currentMoveChosen = currentMoveChosen->child;
	while(currentMoveChosen->rightBrother != 0)
	{
		if(currentMoveChosen->value >= value)
			return *currentMoveChosen;
	}
	while(currentMoveChosen->leftBrother != 0)
	{
		if(currentMoveChosen->value >= value)
			return *currentMoveChosen;
	}
}

//cutoff test check if we reached the lvl required
bool AIPlayer::cutOffTest(AIPlayer::Move *move, int level)
{
	while(move->parent!=0)
	{
		move = move->parent;
		level--;
	}
	std::cerr << "cutoff: " << level << std::endl;
	return level == 0 ? true : false;
}

int AIPlayer::maxValue(AIPlayer::Move *move, int alphaValue, int betaValue, int level)
{
	Piece piece;
	int first = 1;
	int counter = 16;;
	if(Game::isInFinalState(move->m_pieces) || level ==0 )
		return AIPlayer::evalutateState(move->m_pieces);
	int v = MINUS_INFINITY;
	while(Game::thereArePiecesLeft(move->m_pieces) && counter > 0 || level)
	{
		if(first)
		{	//first time the algorothm run it generates a new son
			move->child = new Move;
			move->child->parent = move;
			move = move->child;
			move->leftBrother= 0;
			move->rightBrother =0;
			move->child = 0;
			first = 0;
			std::cerr << "max:  first " << std::endl;
			--counter;
		}
		else
		{	//after that it generates siblings of the son 
			move->rightBrother = new Move;
			move->rightBrother->parent = move->parent;
			move->rightBrother->leftBrother = move;
			move = move->rightBrother;
			move->rightBrother =0;
			move->child = 0;
			std::cerr << "max:  second " << std::endl;
			--counter;
		}
		//find a free piece - chooseAPiece
		for(int i = 0; i<move->parent->m_pieces.size(); i++)
		{
			if(move->parent->m_pieces[i].getLocation().first==16 && move->parent->m_pieces[i].getLocation().second==16)
				piece = move->parent->m_pieces[i];
		}
		//find a random free location
		srand((unsigned)time(0));
		do
		{	
			//select a free space on the board	
			move->location.first = rand()%4;
			move->location.second = rand()%4;
		}
		while(Game::thereIsPiece(move->location));
		move->movedPiece = piece;
		piece.setLocation(move->location);
		move->m_pieces = move->parent->m_pieces;
		move->value = maX(v, (minValue(move, alphaValue, betaValue, --level)));
		if(move->value>= betaValue) 
			return move->value;
		alphaValue = maX(alphaValue, move->value);
	}
	return move->value;

}

int AIPlayer::minValue(AIPlayer::Move *move, int alphaValue, int betaValue, int level)
{
	Location loc;
	Piece piece; 
	int first = 1;
	int counter = 16;
	if(Game::isInFinalState(move->m_pieces) || level ==0 )
		return AIPlayer::evalutateState(move->m_pieces);
	int v = INFINITY;
	while(Game::thereArePiecesLeft(move->m_pieces) && counter >0 || level)
	{
		if(first)
		{	//first time the algorothm run it generates a new son
			move->child = new Move;
			move->child->parent = move;
			move = move->child;
			move->leftBrother= 0;
			move->rightBrother =0;
			move->child = 0;
			first = 0;
			std::cerr << "min:  first " << std::endl;
			--counter;
		}
		else
		{	//after that it generates siblings of the son 
			move->rightBrother = new Move;
			move->rightBrother->parent = move->parent;
			move->rightBrother->leftBrother = move;
			move = move->rightBrother;
			move->rightBrother =0;
			move->child = 0;
			std::cerr << "min:  notfirst " << std::endl;
			--counter;
		}
		//find a free piece - chooseAPiece
		for(int i = 0; i<move->parent->m_pieces.size(); i++)
		{
			if(move->parent->m_pieces[i].getLocation().first==16 && move->parent->m_pieces[i].getLocation().second==16)
				piece = move->parent->m_pieces[i];
		}
		//find a random free location
		srand((unsigned)time(0));
		do
		{	
			//select a free space on the board	
			move->location.first = rand()%4;
			move->location.second = rand()%4;
		}
		while(Game::thereIsPiece(move->location));
		move->movedPiece = piece;
		piece.setLocation(move->location);
		move->m_pieces = move->parent->m_pieces;
		move->value = miN(v, (maxValue(move, alphaValue, betaValue, --level)));
		if(move->value<= alphaValue) 
			return move->value;
		betaValue = miN(betaValue, move->value);
	}
	return move->value;
}

int AIPlayer::evalutateState(std::vector<Piece> & pieces)
{
	int total = 0; //#commonTraits*#piecesInARow
	Piece first, second, third, fourth;
	for(int i = 0; i<4; i++)
	{
		first = AIPlayer::getPieceInPosition(pieces, std::make_pair(i,0));
		second = AIPlayer::getPieceInPosition(pieces, std::make_pair (i,1));
		third = AIPlayer::getPieceInPosition(pieces, std::make_pair (i,2));
		fourth = AIPlayer::getPieceInPosition(pieces, std::make_pair (i,3));
		total += AIPlayer::numberCommonTraits(first,second,third,fourth);		
	}

	for(int i = 0; i<4; i++)
	{
		first = AIPlayer::getPieceInPosition(pieces, std::make_pair(0,i));
		second = AIPlayer::getPieceInPosition(pieces, std::make_pair (1,i));
		third = AIPlayer::getPieceInPosition(pieces, std::make_pair (2,i));
		fourth = AIPlayer::getPieceInPosition(pieces, std::make_pair (3,i));
		total += AIPlayer::numberCommonTraits(first,second,third,fourth);		
	}

	first = AIPlayer::getPieceInPosition(pieces, std::make_pair (0, 0));
	second = AIPlayer::getPieceInPosition(pieces, std::make_pair (1, 1));
	third = AIPlayer::getPieceInPosition(pieces, std::make_pair (2, 2));
	fourth = AIPlayer::getPieceInPosition(pieces, std::make_pair (3, 3));
	total += AIPlayer::numberCommonTraits(first,second,third,fourth);

	first = AIPlayer::getPieceInPosition(pieces, std::make_pair (0, 3));
	second = AIPlayer::getPieceInPosition(pieces, std::make_pair (1, 2));
	third = AIPlayer::getPieceInPosition(pieces, std::make_pair (2, 1));
	fourth = AIPlayer::getPieceInPosition(pieces, std::make_pair (3, 0));
	total += AIPlayer::numberCommonTraits(first,second,third,fourth);
	std::cerr << "evalutate:  total " << total << std::endl;

	return total;
}

Piece AIPlayer::getPieceInPosition(std::vector<Piece> & pieces, Location location)
{
	for(int i =0; i < pieces.size(); i++)
	{
		if(pieces[i].getLocation().first == location.first && pieces[i].getLocation().second == location.second) return pieces[i];
	}
	Piece p;
	return p;

}

Location AIPlayer::chooseALocation(std::vector<Piece> &m_pieces)
{
	Location *loc = new Location();
	srand((unsigned)time(0));
	do
	{	
		//select a free space on the board	
		loc->first = rand()%4;
		loc->second = rand()%4;
	}
	while(Game::thereIsPiece(*loc));
	//setting the new location for the piece
	return *loc;
}

Piece AIPlayer::chooseAPiece(std::vector<Piece> &m_pieces)
{ 
	Piece p;
	for(int i = 0; i<m_pieces.size(); i++)
	{
		if(m_pieces[i].getLocation().first==16 && m_pieces[i].getLocation().second==16)
			return m_pieces[i];
	}
	return p;
}

//check for common traits
int AIPlayer::numberCommonTraits(Piece first, Piece second, Piece third, Piece fourth)
{
	
	if (first.getTrait() != 0xF0)
	{
		if(second.getTrait() != 0xF0)
		{
			if(third.getTrait() != 0xF0)
			{
				if(fourth.getTrait() != 0xF0)
				{
					unsigned char number = (first.getTrait()&second.getTrait()&third.getTrait()&fourth.getTrait()) | (~first.getTrait()&~second.getTrait()&~third.getTrait()&~fourth.getTrait());
					number = number << 4;
					int aux = (int)((number >> 7)&0x01) + (int)((number >> 6)&0x01) + (int)((number >> 5)&0x01) + (int)((number >> 4)&0x01);
					if(aux == 0)return 0;
					else return INFINITY;
				}
				else
				{
					unsigned char number = (first.getTrait()&second.getTrait()&third.getTrait()) | (~first.getTrait()&~second.getTrait()&~third.getTrait());
					number = number << 4;
					int aux = (int)((number >> 7)&0x01) + (int)((number >> 6)&0x01) + (int)((number >> 5)&0x01) + (int)((number >> 4)&0x01);
					return 3*aux;
				}
			}
			else
			{
				if(fourth.getTrait() != 0xF0)
				{
					unsigned char number = (first.getTrait()&second.getTrait()&fourth.getTrait()) | (~first.getTrait()&~second.getTrait()&~fourth.getTrait());
					number = number << 4;
					int aux = (int) ((number >> 7)&0x01) + (int) ((number >> 6)&0x01) + (int)((number >> 5)&0x01) + (int)((number >> 4)&0x01);
					return 3*aux;
				}
				else
				{
					unsigned char number = (first.getTrait()&second.getTrait()) | (~first.getTrait()&~second.getTrait());
					number = number << 4;
					int aux = (int) ((number >> 7)&0x01) + (int) ((number >> 6)&0x01) + (int) ((number >> 5)&0x01) + (int) ((number >> 4)&0x01);
					return 2*aux;
				}
			}
		}
		else
		{
			if(third.getTrait() != 0xF0)
			{
				if(fourth.getTrait() != 0xF0)
				{
					unsigned char number = (first.getTrait()&third.getTrait()&fourth.getTrait()) | (~first.getTrait()&~third.getTrait()&~fourth.getTrait());
					number = number << 4;
					int aux = (int)((number >> 7)&0x01) + (int)((number >> 6)&0x01) + (int)((number >> 5)&0x01) + (int)((number >> 4)&0x01);
					return 3*aux;
				}
				else
				{
					unsigned char number = (first.getTrait()&third.getTrait()) | (~first.getTrait()&~third.getTrait());
					number = number << 4;
					int aux = (int)((number >> 7)&0x01) + (int)((number >> 6)&0x01) + (int)((number >> 5)&0x01) + (int)((number >> 4)&0x01);
					return 2*aux;
				}
			}
			else
			{
				if(fourth.getTrait() != 0xF0)
				{
					unsigned char number = (first.getTrait()&fourth.getTrait()) | (~first.getTrait()&~fourth.getTrait());
					number = number << 4;
					int aux = (int)((number >> 7)&0x01) + (int)((number >> 6)&0x01) + (int)((number >> 5)&0x01) + (int)((number >> 4)&0x01);
					return 2*aux;
				}
				else return 1;
			}
		}
	}
	else
	{
		if(second.getTrait() != 0xF0)
		{
			if(third.getTrait() != 0xF0)
			{
				if(fourth.getTrait() != 0xF0)
				{
					unsigned char number = (second.getTrait()&third.getTrait()&fourth.getTrait()) | (~second.getTrait()&~third.getTrait()&~fourth.getTrait());
					number = number << 4;
					int aux = (int)((number >> 7)&0x01) + (int)((number >> 6)&0x01) + (int)((number >> 5)&0x01) + (int)((number >> 4)&0x01);
					return 3*aux;
				}
				else
				{
					unsigned char number = (second.getTrait()&third.getTrait()) | (~second.getTrait()&~third.getTrait());
					number = number << 4;
					int aux = (int)((number >> 7)&0x01) + (int)((number >> 6)&0x01) + (int)((number >> 5)&0x01) + (int)((number >> 4)&0x01);
					return 2*aux;
				}
			}
			else
			{
				if(fourth.getTrait() != 0xF0)
				{
					unsigned char number = (second.getTrait()&fourth.getTrait()) | (~second.getTrait()&~fourth.getTrait());
					number = number << 4;
					int aux = (int)((number >> 7)&0x01) + (int)((number >> 6)&0x01) + (int)((number >> 5)&0x01) + (int)((number >> 4)&0x01);
					return 2*aux;
				}
				else return 1;
			}
		}
		else
		{
			if(third.getTrait() != 0xF0)
			{
				if(fourth.getTrait() != 0xF0)
				{
					unsigned char number = (third.getTrait()&fourth.getTrait()) | (~third.getTrait()&~fourth.getTrait());
					number = number << 4;
					int aux = (int)((number >> 7)&0x01) + (int)((number >> 6)&0x01) + (int)((number >> 5)&0x01) + (int)((number >> 4)&0x01);
					return 2*aux;
				}
				else return 1;
			}
			else
			{
				if(fourth.getTrait() != 0xF0)
				{
					return 1;
				}
				else return 0;
			}
		}
	}
	return 0;
}
